1use crate::scripts::base::*;
2use crate::types::*;
3use crate::utils::encoding::*;
4use crate::utils::escape::*;
5use anyhow::Result;
6use std::collections::HashSet;
7use std::ops::{Deref, DerefMut};
8use std::sync::Arc;
9use unicode_segmentation::UnicodeSegmentation;
10
11#[derive(Debug, Clone)]
12pub struct TxtBuilder {}
14
15impl TxtBuilder {
16 pub const fn new() -> Self {
18 Self {}
19 }
20}
21
22impl ScriptBuilder for TxtBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Utf8
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script>> {
36 Ok(Box::new(TxtScript::new(buf, encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["txt"]
41 }
42
43 fn script_type(&self) -> &'static ScriptType {
44 &ScriptType::ArtemisPanmimisoftTxt
45 }
46}
47
48pub trait Node {
50 fn serialize(&self) -> String;
52}
53
54#[derive(Debug, Clone, PartialEq)]
55pub struct CommentNode(pub String);
57
58impl Node for CommentNode {
59 fn serialize(&self) -> String {
60 format!("//{}", self.0)
61 }
62}
63
64#[derive(Clone, Debug, PartialEq)]
65pub struct EmptyLineNode;
67
68impl Node for EmptyLineNode {
69 fn serialize(&self) -> String {
70 String::new()
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub struct LabelNode(pub String);
77
78impl Node for LabelNode {
79 fn serialize(&self) -> String {
80 format!("*{}", self.0)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq)]
85pub struct TagNode {
87 pub name: String,
89 pub attributes: Vec<(String, Option<String>)>,
91}
92
93impl Node for TagNode {
94 fn serialize(&self) -> String {
95 let attributes = self
96 .attributes
97 .iter()
98 .map(|(key, value)| {
99 if let Some(val) = value {
100 format!("{}=\"{}\"", key, val)
101 } else {
102 key.clone()
103 }
104 })
105 .collect::<Vec<_>>()
106 .join(" ");
107 if attributes.is_empty() {
108 format!("[{}]", self.name)
109 } else {
110 format!("[{} {}]", self.name, attributes)
111 }
112 }
113}
114
115impl TagNode {
116 fn ser_attributes_xml(&self) -> String {
117 let mut parts = Vec::new();
118 for (key, value) in self.attributes.iter() {
119 match value {
120 None => {
121 parts.push(key.clone());
122 }
123 Some(val) => {
124 parts.push(format!("{}=\"{}\"", key, escape_xml_attr_value(val)));
125 }
126 }
127 }
128 parts.join(" ")
129 }
130
131 pub fn get_attr(&self, attr: &str) -> Option<&str> {
133 self.attributes
134 .iter()
135 .find(|(key, _)| key == attr)
136 .and_then(|(_, value)| value.as_deref())
137 }
138
139 pub fn is_blocked_name(&self, set: &HashSet<String>) -> bool {
141 self.name.is_ascii() || set.contains(&self.name)
142 }
143
144 pub fn has_attr(&self, attr: &str) -> bool {
146 self.attributes.iter().any(|(key, _)| key == attr)
147 }
148
149 pub fn set_attr(&mut self, attr: &str, value: Option<String>) {
151 if let Some(pos) = self.attributes.iter().position(|(key, _)| key == attr) {
152 self.attributes[pos].1 = value;
153 } else {
154 self.attributes.push((attr.to_string(), value));
155 }
156 }
157
158 pub fn to_xml(&self) -> String {
160 let attributes = self.ser_attributes_xml();
161 if attributes.is_empty() {
162 format!("<{}>", self.name)
163 } else {
164 format!("<{} {}>", self.name, attributes)
165 }
166 }
167}
168
169#[derive(Debug, Clone, PartialEq)]
170pub struct TextNode(pub String);
172
173#[derive(Debug, Clone, PartialEq)]
174pub enum TxtLineNode {
176 Comment(CommentNode),
177 Tag(TagNode),
178 Text(TextNode),
179}
180
181impl TxtLineNode {
182 pub fn is_comment(&self) -> bool {
184 matches!(self, TxtLineNode::Comment(_))
185 }
186
187 pub fn is_tag(&self, tag: &str) -> bool {
191 matches!(self, TxtLineNode::Tag(node) if node.name == tag)
192 }
193
194 pub fn is_tag_blocked_name(&self, set: &HashSet<String>) -> bool {
196 if let TxtLineNode::Tag(node) = self {
197 node.is_blocked_name(set)
198 } else {
199 false
200 }
201 }
202
203 pub fn tag_attr_keys<'a>(&'a self) -> Box<dyn Iterator<Item = &'a str> + 'a> {
205 if let TxtLineNode::Tag(node) = self {
206 Box::new(node.attributes.iter().map(|(key, _)| key.as_str()))
207 } else {
208 Box::new(std::iter::empty())
209 }
210 }
211
212 pub fn tag_get_attr<'a>(&'a self, attr: &str) -> Option<&'a str> {
213 if let TxtLineNode::Tag(node) = self {
214 node.get_attr(attr)
215 } else {
216 None
217 }
218 }
219
220 pub fn tag_has_attr(&self, attr: &str) -> bool {
222 if let TxtLineNode::Tag(node) = self {
223 node.attributes.iter().any(|(key, _)| key == attr)
224 } else {
225 false
226 }
227 }
228
229 pub fn tag_set_attr(&mut self, attr: &str, value: Option<String>) {
230 if let TxtLineNode::Tag(node) = self {
231 node.set_attr(attr, value);
232 }
233 }
234
235 pub fn to_xml(&self) -> String {
237 match self {
238 TxtLineNode::Comment(_) => String::new(), TxtLineNode::Tag(n) => {
240 if (n.name == "rt2" || n.name == "ret2") && n.attributes.is_empty() {
241 "\n".to_string()
242 } else {
243 n.to_xml()
244 }
245 }
246 TxtLineNode::Text(n) => escape_xml_text_value(&n.0),
247 }
248 }
249}
250
251impl Node for TxtLineNode {
252 fn serialize(&self) -> String {
253 match self {
254 TxtLineNode::Comment(node) => node.serialize(),
255 TxtLineNode::Tag(node) => node.serialize(),
256 TxtLineNode::Text(node) => node.0.clone(),
257 }
258 }
259}
260
261#[derive(Debug, Clone, PartialEq)]
262pub struct TxtLine(pub Vec<TxtLineNode>);
264
265impl Deref for TxtLine {
266 type Target = Vec<TxtLineNode>;
267
268 fn deref(&self) -> &Self::Target {
269 &self.0
270 }
271}
272
273impl DerefMut for TxtLine {
274 fn deref_mut(&mut self) -> &mut Self::Target {
275 &mut self.0
276 }
277}
278
279impl Node for TxtLine {
280 fn serialize(&self) -> String {
281 self.0
282 .iter()
283 .map(|node| node.serialize())
284 .collect::<Vec<_>>()
285 .join("")
286 }
287}
288
289impl TxtLine {
290 pub fn to_xml(&self) -> String {
292 self.0
293 .iter()
294 .map(|node| node.to_xml())
295 .collect::<Vec<_>>()
296 .join("")
297 }
298}
299
300#[derive(Debug, Clone, PartialEq)]
301pub struct LineTag {
302 pub name: String,
303 pub list: Vec<String>,
304}
305
306impl Node for LineTag {
307 fn serialize(&self) -> String {
308 let list = self.list.join(",");
309 format!("#{} {}", self.name, list)
310 }
311}
312
313#[derive(Debug, Clone, PartialEq)]
314pub enum ParsedLine {
316 Empty(EmptyLineNode),
318 Comment(CommentNode),
320 Label(LabelNode),
322 Line(TxtLine),
324 LineTag(LineTag),
326 Comment2(CommentNode),
328}
329
330impl Node for ParsedLine {
331 fn serialize(&self) -> String {
332 match self {
333 ParsedLine::Empty(node) => node.serialize(),
334 ParsedLine::Comment(node) => node.serialize(),
335 ParsedLine::Label(node) => node.serialize(),
336 ParsedLine::Line(line) => line.serialize(),
337 ParsedLine::LineTag(node) => node.serialize(),
338 ParsedLine::Comment2(node) => format!(";>>{}", node.0),
339 }
340 }
341}
342
343impl ParsedLine {
344 pub fn len(&self) -> usize {
346 match self {
347 ParsedLine::Empty(_) => 0,
348 ParsedLine::Comment(_) => 0,
349 ParsedLine::Label(_) => 0,
350 ParsedLine::Line(line) => line.len(),
351 ParsedLine::LineTag(_) => 0,
352 ParsedLine::Comment2(_) => 0,
353 }
354 }
355
356 pub fn push(&mut self, node: TxtLineNode) {
358 if let ParsedLine::Line(line) = self {
359 line.push(node);
360 } else {
361 }
363 }
364
365 pub fn insert(&mut self, index: usize, node: TxtLineNode) {
367 if let ParsedLine::Line(line) = self {
368 line.insert(index, node);
369 } else {
370 }
372 }
373
374 pub fn remove(&mut self, index: usize) -> Option<TxtLineNode> {
376 if let ParsedLine::Line(line) = self {
377 if index < line.len() {
378 Some(line.remove(index))
379 } else {
380 None
381 }
382 } else {
383 None
385 }
386 }
387}
388
389#[derive(Debug, Clone)]
390pub struct ParsedScript(pub Vec<ParsedLine>);
392
393impl Deref for ParsedScript {
394 type Target = Vec<ParsedLine>;
395
396 fn deref(&self) -> &Self::Target {
397 &self.0
398 }
399}
400
401impl DerefMut for ParsedScript {
402 fn deref_mut(&mut self) -> &mut Self::Target {
403 &mut self.0
404 }
405}
406
407impl Node for ParsedScript {
408 fn serialize(&self) -> String {
409 self.0
410 .iter()
411 .map(|line| line.serialize())
412 .collect::<Vec<_>>()
413 .join("\n")
414 }
415}
416
417pub struct Parser {
419 lines: Vec<String>,
420}
421
422impl Parser {
423 pub fn new<S: AsRef<str> + ?Sized>(script: &S) -> Self {
424 let lines = script.as_ref().lines().map(|s| s.to_string()).collect();
425 Self { lines }
426 }
427
428 pub fn parse(&self, preserve_empty_lines: bool) -> Result<ParsedScript> {
429 let mut parsed_script = Vec::new();
430 let mut i = 0;
431 let line_count = self.lines.len();
432 while i < line_count {
433 let line = self.lines[i].trim();
434 i += 1;
435 if line.is_empty() {
436 if preserve_empty_lines {
437 parsed_script.push(ParsedLine::Empty(EmptyLineNode));
438 }
439 continue;
440 }
441 if line.starts_with("//") {
442 parsed_script.push(ParsedLine::Comment(CommentNode(line[2..].to_string())));
443 continue;
444 }
445 if line.starts_with("*") {
446 let label = line[1..].trim().to_string();
447 parsed_script.push(ParsedLine::Label(LabelNode(label)));
448 continue;
449 }
450 if line.starts_with("#") {
451 let rest = line[1..].trim();
452 let mut parts = rest.splitn(2, ' ');
453 let name = parts
454 .next()
455 .ok_or(anyhow::anyhow!("Invalid line tag: {}", line))?
456 .to_string();
457 let list = if let Some(list_str) = parts.next() {
458 list_str
459 .split(',')
460 .map(|s| s.trim().to_string())
461 .collect::<Vec<_>>()
462 } else {
463 Vec::new()
464 };
465 parsed_script.push(ParsedLine::LineTag(LineTag { name, list }));
466 continue;
467 }
468 if line.starts_with(";>>") {
469 parsed_script.push(ParsedLine::Comment2(CommentNode(line[3..].to_string())));
470 continue;
471 }
472 let mut temp = String::new();
473 let mut nodes = Vec::new();
474 let mut line_graphs = line.graphemes(true).collect::<Vec<_>>();
475 let mut line_pos = 0;
476 let mut is_comment = false;
477 while line_pos < line_graphs.len() {
478 let graph = line_graphs[line_pos];
479 line_pos += 1;
480 temp.push_str(graph);
481 if is_comment {
482 continue;
483 }
484 if !is_comment && temp.ends_with("//") && temp.len() > 2 {
485 nodes.push(TxtLineNode::Text(TextNode(
486 temp[..temp.len() - 2].to_string(),
487 )));
488 temp.clear();
489 is_comment = true;
490 continue;
491 }
492 if graph == "[" {
493 if !temp.trim_end_matches("[").is_empty() {
494 nodes.push(TxtLineNode::Text(TextNode(
495 temp.trim_end_matches("[").to_string(),
496 )));
497 }
498 while !line_graphs[line_pos..].contains(&"]") {
500 if i < line_count {
501 let nline = self.lines[i].trim();
502 i += 1;
503 line_graphs.push("\n");
505 line_graphs.extend(nline.graphemes(true));
506 } else {
507 break;
508 }
509 }
510 let (tag, nextpos) = TagParser {
511 graphs: &line_graphs,
512 pos: line_pos,
513 }
514 .parse()?;
515 line_pos = nextpos;
516 nodes.push(TxtLineNode::Tag(tag));
517 temp.clear();
518 continue;
519 }
520 }
521 if is_comment {
522 nodes.push(TxtLineNode::Comment(CommentNode(temp)));
523 } else {
524 if !temp.is_empty() {
525 nodes.push(TxtLineNode::Text(TextNode(temp)));
526 }
527 }
528 parsed_script.push(ParsedLine::Line(TxtLine(nodes)));
529 }
530 Ok(ParsedScript(parsed_script))
531 }
532}
533
534struct TagParser<'a> {
535 graphs: &'a [&'a str],
536 pos: usize,
537}
538
539impl<'a> TagParser<'a> {
540 fn peek(&self) -> Option<&'a str> {
541 self.graphs.get(self.pos).cloned()
542 }
543
544 fn eat(&mut self) {
545 if self.pos < self.graphs.len() {
546 self.pos += 1;
547 }
548 }
549
550 fn next(&mut self) -> Option<&'a str> {
551 if self.pos < self.graphs.len() {
552 let graph = self.graphs[self.pos];
553 self.pos += 1;
554 Some(graph)
555 } else {
556 None
557 }
558 }
559
560 fn is_indent(&self, indent: &str) -> bool {
561 let mut pos = self.pos;
562 for ident in indent.graphemes(true) {
563 if pos >= self.graphs.len() || self.graphs[pos] != ident {
564 return false;
565 }
566 pos += 1;
567 }
568 true
569 }
570
571 fn eat_all_equal(&mut self) {
572 while let Some(graph) = self.peek() {
573 if graph == "=" {
574 self.eat();
575 } else {
576 break;
577 }
578 }
579 }
580
581 fn parse(&mut self) -> Result<(TagNode, usize)> {
582 let name = self.parse_tag()?;
583 self.erase_whitespace();
584 let mut attributes = Vec::new();
585 loop {
586 let graph = match self.peek() {
587 Some(g) => g,
588 None => {
589 return Err(anyhow::anyhow!(
590 "Unexpected end of tag parsing: {}",
591 self.graphs.join("")
592 ));
593 }
594 };
595 if graph == "]" {
596 self.eat();
597 break;
598 }
599 if graph == " " || graph == "\t" {
600 self.eat();
601 continue;
602 }
603 if graph == "=" {
604 return Err(anyhow::anyhow!("Unexpected '=' without attribute name"));
605 }
606 let attr_name = self.parse_attr_name()?;
607 self.erase_whitespace();
608 let graph = match self.peek() {
609 Some(g) => g,
610 None => {
611 return Err(anyhow::anyhow!(
612 "Unexpected end of tag parsing: {}",
613 self.graphs.join("")
614 ));
615 }
616 };
617 if graph == "]" {
618 self.eat();
619 attributes.push((attr_name, None));
620 break;
621 }
622 if graph == "=" {
623 self.eat_all_equal();
627 self.erase_whitespace();
628 let value = self.parse_attr_value()?;
629 attributes.push((attr_name, Some(value)));
630 self.erase_whitespace();
631 } else {
632 attributes.push((attr_name, None));
633 self.erase_whitespace();
634 continue;
635 }
636 }
637 return Ok((TagNode { name, attributes }, self.pos));
638 }
639
640 fn erase_whitespace(&mut self) {
641 while let Some(graph) = self.peek() {
642 if graph == " " || graph == "\t" {
643 self.eat();
644 } else {
645 break;
646 }
647 }
648 }
649
650 fn parse_attr_name(&mut self) -> Result<String> {
651 let mut attr_name = String::new();
652 while let Some(graph) = self.peek() {
653 if graph == "=" || graph == " " || graph == "\t" || graph == "]" {
654 break;
655 }
656 attr_name.push_str(graph);
657 self.eat();
658 }
659 if attr_name.is_empty() {
660 return Err(anyhow::anyhow!("Empty attribute name found"));
661 }
662 Ok(attr_name)
663 }
664
665 fn parse_attr_value(&mut self) -> Result<String> {
666 let mut value = String::new();
667 if !self.is_indent("\"") {
668 return Err(anyhow::anyhow!(
669 "Expected attribute value to start with a quote: {}",
670 self.graphs.join("")
671 ));
672 }
673 self.eat(); while let Some(graph) = self.next() {
675 if graph == "\"" {
676 break; }
678 value.push_str(graph);
679 }
680 Ok(value)
681 }
682
683 fn parse_tag(&mut self) -> Result<String> {
684 let mut tag = String::new();
685 while let Some(graph) = self.peek() {
686 if graph == " " || graph == "\t" || graph == "]" {
687 break;
688 }
689 tag.push_str(graph);
690 self.eat();
691 }
692 if tag.is_empty() {
693 return Err(anyhow::anyhow!("Empty tag found"));
694 }
695 Ok(tag)
696 }
697}
698
699struct XMLTextParser<'a> {
700 str: &'a str,
701 lang: &'a str,
702 pos: usize,
703 no_rt2: bool,
704}
705
706impl<'a> XMLTextParser<'a> {
707 pub fn new(text: &'a str, lang: &'a str, no_rt2: bool) -> Self {
708 Self {
709 str: text,
710 lang,
711 pos: 0,
712 no_rt2,
713 }
714 }
715
716 fn parse_tag(&mut self) -> Result<TagNode> {
717 let mut name = String::new();
718 let mut attributes = Vec::new();
719 let mut is_name = true;
720 let mut is_key = false;
721 let mut is_value = false;
722 let mut is_in_quote = false;
723 let mut key = String::new();
724 let mut value = String::new();
725 while let Some(c) = self.next() {
726 match c {
727 '>' => {
728 if !name.is_empty() {
729 return Ok(TagNode { name, attributes });
730 } else {
731 return Err(anyhow::anyhow!("Empty tag name"));
732 }
733 }
734 ' ' | '\t' => {
735 if is_name {
736 is_name = false;
737 is_key = true;
738 } else if is_key {
739 if !key.is_empty() {
740 attributes.push((key.clone(), None));
741 key.clear();
742 }
743 } else if is_value {
744 if is_in_quote {
745 value.push(c);
746 } else {
747 if !value.is_empty() {
748 attributes.push((key.clone(), Some(unescape_xml(&value))));
749 key.clear();
750 value.clear();
751 }
752 is_key = true;
753 is_value = false;
754 }
755 }
756 }
757 '"' => {
758 if is_in_quote {
759 is_in_quote = false;
760 if !value.is_empty() {
761 attributes.push((key.clone(), Some(unescape_xml(&value))));
762 key.clear();
763 value.clear();
764 }
765 is_key = true;
766 } else {
767 is_in_quote = true;
768 }
769 }
770 '=' => {
771 if is_key {
772 is_key = false;
773 is_value = true;
774 }
775 }
776 _ => {
777 if is_name {
778 name.push(c);
779 } else if is_key {
780 key.push(c);
781 } else if is_value {
782 value.push(c);
783 } else {
784 return Err(anyhow::anyhow!("Unexpected character in tag: {}", c));
785 }
786 }
787 }
788 }
789 Err(anyhow::anyhow!("Unexpected end of input while parsing tag"))
790 }
791
792 pub fn parse(mut self) -> Result<Vec<ParsedLine>> {
793 let mut lines = Vec::new();
794 let mut current_line = Vec::new();
795 let mut text = String::new();
796 if !self.no_rt2 {
797 current_line.push(TxtLineNode::Tag(TagNode {
798 name: "lang".to_string(),
799 attributes: vec![(self.lang.to_string(), None)],
800 }));
801 }
802 while let Some(c) = self.next() {
803 match c {
804 '<' => {
805 if !text.is_empty() {
806 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
807 text.clear();
808 }
809 let tag = self.parse_tag()?;
810 let is_r = tag.name == "rt2" || tag.name == "ret2";
811 current_line.push(TxtLineNode::Tag(tag));
812 if is_r {
813 lines.push(ParsedLine::Line(TxtLine(current_line)));
814 current_line = Vec::new();
815 }
816 }
817 '\n' => {
818 if !text.is_empty() {
819 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
820 text.clear();
821 }
822 if !self.no_rt2 {
823 current_line.push(TxtLineNode::Tag(TagNode {
824 name: "rt2".to_string(),
825 attributes: Vec::new(),
826 }));
827 }
828 lines.push(ParsedLine::Line(TxtLine(current_line)));
829 current_line = Vec::new();
830 }
831 _ => text.push(c),
832 }
833 }
834 if !text.is_empty() {
835 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
836 }
837 if !self.no_rt2 {
838 current_line.push(TxtLineNode::Tag(TagNode {
839 name: "/lang".to_string(),
840 attributes: Vec::new(),
841 }));
842 }
843 lines.push(ParsedLine::Line(TxtLine(current_line)));
844 Ok(lines)
845 }
846
847 fn next(&mut self) -> Option<char> {
848 if self.pos < self.str.len() {
849 let c = self.str[self.pos..].chars().next()?;
850 self.pos += c.len_utf8();
851 Some(c)
852 } else {
853 None
854 }
855 }
856}
857
858#[derive(Debug)]
859pub struct TxtScript {
860 tree: ParsedScript,
861 blacklist_names: Arc<HashSet<String>>,
862 lang: Option<String>,
863 multi_lang: bool,
864}
865
866impl TxtScript {
867 pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
869 let script = decode_to_string(encoding, &buf, true)?;
870 let parser = Parser::new(&script);
871 let tree = parser.parse(true)?;
872 Ok(Self {
873 tree,
874 blacklist_names: config.artemis_panmimisoft_txt_blacklist_names.clone(),
875 lang: config.artemis_panmimisoft_txt_lang.clone(),
876 multi_lang: config.artemis_panmimisoft_txt_multi_lang,
877 })
878 }
879}
880
881impl Script for TxtScript {
882 fn default_output_script_type(&self) -> OutputScriptType {
883 OutputScriptType::Json
884 }
885
886 fn default_format_type(&self) -> FormatOptions {
887 FormatOptions::None
888 }
889
890 fn extract_messages(&self) -> Result<Vec<Message>> {
891 let mut messages = Vec::new();
892 let mut i = 0;
893 let len = self.tree.len();
894 let mut last_tag_block: Option<TagNode> = None;
895 let mut lang = self.lang.as_ref().map(|s| s.as_str());
896 let mut message = TxtLine(Vec::new());
897 let mut in_lang_block = false;
898 let mut droped_lang_block = false;
899 let mut is_selectblk = false;
900 let mut has_printlang = false;
901 for line in &self.tree.0 {
902 if let ParsedLine::Line(txt_line) = line {
903 for node in txt_line.iter() {
904 if node.is_tag("printlang") {
905 has_printlang = true;
906 break;
907 }
908 }
909 }
910 }
911 if !has_printlang {
912 let mut message_started = false;
913 let mut adv_started = false;
914 while i < len {
915 let line = &self.tree[i];
916 match line {
917 ParsedLine::Empty(_) => {
918 let mes = message.to_xml();
919 message.clear();
920 message_started = false;
921 if !mes.is_empty() && adv_started {
922 let name = match &last_tag_block {
923 Some(block) => Some(if let Some(name) = block.get_attr("name") {
924 name.to_string()
925 } else {
926 block.name.clone()
927 }),
928 _ => None,
929 };
930 messages.push(Message { name, message: mes });
931 }
932 last_tag_block = None;
933 }
934 ParsedLine::Line(line) => {
935 if !message.is_empty() && message_started {
936 message.push(TxtLineNode::Tag(TagNode {
937 name: "rt2".into(),
938 attributes: vec![],
939 }));
940 }
941 for node in line.iter() {
942 if node.is_tag("adv") {
943 adv_started = true;
944 } else if node.is_tag("selectbtn_init") {
945 is_selectblk = true;
946 } else if node.is_tag("selectbtn") {
947 let text = node.tag_get_attr("text").ok_or(anyhow::anyhow!(
948 "No text attribute found in selectbtn tag"
949 ))?;
950 messages.push(Message {
951 name: None,
952 message: text.to_string(),
953 });
954 } else if node.is_tag("/selectbtn") {
955 is_selectblk = false;
956 } else if let TxtLineNode::Tag(tag) = node {
957 if !message_started {
958 if !tag.is_blocked_name(&self.blacklist_names) {
959 last_tag_block = Some(tag.clone());
960 }
961 } else {
962 message.push(node.clone());
963 }
964 } else if node.is_comment() {
965 } else {
967 message_started = true;
968 message.push(node.clone());
969 }
970 }
971 }
972 _ => {}
973 }
974 i += 1;
975 }
976 }
977 while i < len {
978 let line = &self.tree[i];
979 if let ParsedLine::Line(line) = line {
980 for node in line.iter() {
981 if node.is_tag("lang") {
982 let lan = match lang {
983 Some(l) => l,
984 None => {
985 for key in node.tag_attr_keys() {
986 lang = Some(key);
987 break;
988 }
989 match lang {
990 Some(l) => l,
991 None => {
992 return Err(anyhow::anyhow!(
993 "No language found in lang tag"
994 ));
995 }
996 }
997 }
998 };
999 if node.tag_has_attr(lan) {
1000 in_lang_block = true;
1001 } else {
1002 droped_lang_block = true;
1003 }
1004 } else if node.is_tag("/lang") {
1005 in_lang_block = false;
1006 droped_lang_block = false;
1007 } else if node.is_tag("printlang") {
1008 let mes = message.to_xml();
1009 message.clear();
1010 if !mes.is_empty() {
1011 let name = match &last_tag_block {
1012 Some(block) => Some(if let Some(name) = block.get_attr("name") {
1013 name.to_string()
1014 } else {
1015 block.name.clone()
1016 }),
1017 _ => None,
1018 };
1019 messages.push(Message { name, message: mes });
1020 }
1021 last_tag_block = None;
1022 } else if node.is_tag("selectbtn_init") {
1023 is_selectblk = true;
1024 } else if node.is_tag("selectbtn") {
1025 let mut lan = match lang {
1026 Some(l) => l,
1027 None => {
1028 for key in node.tag_attr_keys() {
1029 if key == "label" || key == "call" {
1030 continue;
1031 }
1032 lang = Some(key);
1033 break;
1034 }
1035 match lang {
1036 Some(l) => l,
1037 None => {
1038 return Err(anyhow::anyhow!(
1039 "No language found in selectbtn tag"
1040 ));
1041 }
1042 }
1043 }
1044 };
1045 if !node.tag_has_attr(lan) {
1046 for key in node.tag_attr_keys() {
1047 if key == "label" || key == "call" {
1048 continue;
1049 }
1050 lan = key;
1051 break;
1052 }
1053 }
1054 if let Some(t) = node.tag_get_attr(lan) {
1055 messages.push(Message {
1056 name: None,
1057 message: t.to_string(),
1058 });
1059 }
1060 } else if node.is_tag("/selectbtn") {
1061 is_selectblk = false;
1062 } else if in_lang_block {
1063 message.push(node.clone());
1064 } else if droped_lang_block {
1065 } else if is_selectblk {
1067 } else if let TxtLineNode::Tag(tag) = node {
1069 if !tag.is_blocked_name(&self.blacklist_names) {
1070 last_tag_block = Some(tag.clone());
1071 }
1072 }
1073 }
1074 }
1075 i += 1;
1076 }
1077 Ok(messages)
1078 }
1079
1080 fn import_messages<'a>(
1081 &'a self,
1082 messages: Vec<Message>,
1083 mut file: Box<dyn WriteSeek + 'a>,
1084 _filename: &str,
1085 encoding: Encoding,
1086 replacement: Option<&'a ReplacementTable>,
1087 ) -> Result<()> {
1088 let mut output = self.tree.clone();
1089 let mut current_line = 0;
1090 let mut last_tag_block_loc = None;
1091 let mut lang = self.lang.clone();
1092 let mut mes = messages.iter();
1093 let mut mess = mes.next();
1094 let mut lang_block_index = None;
1095 let mut lang_end_block_index = None;
1096 let mut in_lang_block = false;
1097 let mut droped_lang_block = false;
1098 let mut is_selectblk = false;
1099 let mut has_printlang = false;
1100 for line in &output.0 {
1101 if let ParsedLine::Line(txt_line) = line {
1102 for node in txt_line.iter() {
1103 if node.is_tag("printlang") {
1104 has_printlang = true;
1105 break;
1106 }
1107 }
1108 }
1109 }
1110 if !has_printlang {
1111 let mut adv_started = false;
1112 let mut started_line: Option<usize> = None;
1113 if self.multi_lang && lang.is_none() {
1114 return Err(anyhow::anyhow!(
1115 "Multi-language import requires a specified language. Use --artemis-panmimisoft-txt-lang to set the language."
1116 ));
1117 } else if !self.multi_lang {
1118 lang = None;
1119 }
1120 while current_line < output.len() {
1121 let line = output[current_line].clone();
1122 match &line {
1123 ParsedLine::Empty(_) => {
1124 if adv_started {
1125 if let Some(start) = started_line {
1126 let m = match mess {
1127 Some(m) => m,
1128 None => {
1129 return Err(anyhow::anyhow!("Not enough messages."));
1130 }
1131 };
1132 let mut message = m.message.clone();
1133 if let Some(repl) = replacement {
1134 for (k, v) in &repl.map {
1135 message = message.replace(k, v);
1136 }
1137 }
1138 if let Some(name) = &m.name {
1139 let block_index: (usize, usize) =
1140 match last_tag_block_loc.take() {
1141 Some(data) => data,
1142 None => {
1143 return Err(anyhow::anyhow!(
1144 "No name tag block found before message."
1145 ));
1146 }
1147 };
1148 let mut name = name.clone();
1149 if let Some(repl) = replacement {
1150 for (k, v) in &repl.map {
1151 name = name.replace(k, v);
1152 }
1153 }
1154 let mblock = &mut output[block_index.0];
1155 if let ParsedLine::Line(txt_line) = mblock {
1156 let block = txt_line[block_index.1].clone();
1157 if let TxtLineNode::Tag(mut tag) = block {
1158 tag.set_attr("name", Some(name));
1159 txt_line[block_index.1] = TxtLineNode::Tag(tag);
1160 } else {
1161 return Err(anyhow::anyhow!(
1162 "Last tag block is not a tag: {:?}",
1163 mblock
1164 ));
1165 }
1166 } else {
1167 return Err(anyhow::anyhow!(
1168 "Last tag block is not a line: {:?}",
1169 mblock
1170 ));
1171 }
1172 }
1173 let mut nodes = XMLTextParser::new(
1174 &message,
1175 lang.as_ref().map(|s| s.as_str()).unwrap_or(""),
1176 !self.multi_lang,
1177 )
1178 .parse()?;
1179 if self.multi_lang {
1180 nodes.push(ParsedLine::Line(TxtLine(vec![TxtLineNode::Tag(
1182 TagNode {
1183 name: "printlang".to_string(),
1184 attributes: Vec::new(),
1185 },
1186 )])));
1187 }
1188 let ori_len = (current_line - start) as isize;
1189 let new_len = nodes.len() as isize;
1190 for _ in start..current_line {
1191 output.remove(start);
1192 }
1193 let mut start_index = start;
1194 for node in nodes {
1195 output.insert(start_index, node);
1196 start_index += 1;
1197 }
1198 current_line = (current_line as isize + new_len - ori_len) as usize;
1199 mess = mes.next();
1200 }
1201 }
1202 started_line = None;
1203 last_tag_block_loc = None;
1204 }
1205 ParsedLine::Line(line) => {
1206 for (i, node) in line.iter().enumerate() {
1207 if node.is_tag("adv") {
1208 adv_started = true;
1209 } else if node.is_tag("selectbtn_init") {
1210 is_selectblk = true;
1211 } else if node.is_tag("selectbtn") {
1212 let m = match mess {
1213 Some(m) => m,
1214 None => {
1215 return Err(anyhow::anyhow!("Not enough messages."));
1216 }
1217 };
1218 let mut message = m.message.clone();
1219 if let Some(repl) = replacement {
1220 for (k, v) in &repl.map {
1221 message = message.replace(k, v);
1222 }
1223 }
1224 let mut node = node.clone();
1225 node.tag_set_attr(
1226 lang.as_ref().map(|s| s.as_str()).unwrap_or("text"),
1227 Some(message),
1228 );
1229 let block = &mut output[current_line];
1230 if let ParsedLine::Line(txt_line) = block {
1231 txt_line[i] = node;
1232 } else {
1233 return Err(anyhow::anyhow!(
1234 "Selectbtn line is not a line: {:?}",
1235 block
1236 ));
1237 }
1238 mess = mes.next();
1239 } else if let TxtLineNode::Tag(tag) = node {
1240 if started_line.is_none() {
1241 if !tag.is_blocked_name(&self.blacklist_names) {
1242 last_tag_block_loc = Some((current_line, i));
1243 }
1244 }
1245 } else if node.is_comment() {
1246 } else {
1248 if started_line.is_none() {
1249 started_line = Some(current_line);
1250 }
1251 }
1252 }
1253 }
1254 _ => {}
1255 }
1256 current_line += 1;
1257 }
1258 }
1259 while current_line < output.len() {
1260 let line = output[current_line].clone();
1261 if let ParsedLine::Line(line) = &line {
1262 for (i, node) in line.iter().enumerate() {
1263 if node.is_tag("lang") {
1264 let lan = match lang.as_ref() {
1265 Some(l) => l.as_str(),
1266 None => {
1267 for key in node.tag_attr_keys() {
1268 lang = Some(key.to_string());
1269 break;
1270 }
1271 match lang.as_ref() {
1272 Some(l) => l.as_str(),
1273 None => {
1274 return Err(anyhow::anyhow!(
1275 "No language found in lang tag"
1276 ));
1277 }
1278 }
1279 }
1280 };
1281 if node.tag_has_attr(lan) {
1282 in_lang_block = true;
1283 lang_block_index = Some((current_line, i));
1284 } else {
1285 droped_lang_block = true;
1286 }
1287 } else if node.is_tag("/lang") {
1288 if in_lang_block {
1289 lang_end_block_index = Some((current_line, i));
1290 }
1291 in_lang_block = false;
1292 droped_lang_block = false;
1293 } else if node.is_tag("printlang") {
1294 let lan = lang
1295 .as_ref()
1296 .map(|s| s.as_str())
1297 .ok_or(anyhow::anyhow!("No language specified."))?;
1298 let m = match mess {
1299 Some(m) => m,
1300 None => {
1301 return Err(anyhow::anyhow!("Not enough messages."));
1302 }
1303 };
1304 if let Some(name) = &m.name {
1305 let block_index: (usize, usize) = match last_tag_block_loc.take() {
1306 Some(data) => data,
1307 None => {
1308 return Err(anyhow::anyhow!(
1309 "No name tag block found before printlang.",
1310 ));
1311 }
1312 };
1313 let mut name = name.clone();
1314 if let Some(repl) = replacement {
1315 for (k, v) in &repl.map {
1316 name = name.replace(k, v);
1317 }
1318 }
1319 let mblock = &mut output[block_index.0];
1320 if let ParsedLine::Line(txt_line) = mblock {
1321 let block = txt_line[block_index.1].clone();
1322 if let TxtLineNode::Tag(mut tag) = block {
1323 tag.set_attr("name", Some(name));
1324 txt_line[block_index.1] = TxtLineNode::Tag(tag);
1325 } else {
1326 return Err(anyhow::anyhow!(
1327 "Last tag block is not a tag: {:?}",
1328 mblock
1329 ));
1330 }
1331 } else {
1332 return Err(anyhow::anyhow!(
1333 "Last tag block is not a line: {:?}",
1334 mblock
1335 ));
1336 }
1337 }
1338 let mut message = m.message.clone();
1339 if let Some(repl) = replacement {
1340 for (k, v) in &repl.map {
1341 message = message.replace(k, v);
1342 }
1343 }
1344 let mut nodes = XMLTextParser::new(&message, lan, false).parse()?;
1345 if lang_block_index.is_some() && lang_end_block_index.is_some() {
1346 let start_index = lang_block_index.unwrap();
1347 let end_index = lang_end_block_index.unwrap();
1348 if start_index.1 != 0 {
1349 let block = output[start_index.0].clone();
1350 if let ParsedLine::Line(txt_line) = block {
1351 for i in 0..start_index.1 {
1352 nodes[0].insert(i, txt_line[i].clone());
1353 }
1354 } else {
1355 return Err(anyhow::anyhow!(
1356 "Lang block start is not a line: {:?}",
1357 block
1358 ));
1359 }
1360 }
1361 if end_index.1 + 1 < output[end_index.0].len() {
1362 let block = output[end_index.0].clone();
1363 if let ParsedLine::Line(txt_line) = block {
1364 for i in end_index.1 + 1..txt_line.len() {
1365 nodes.last_mut().unwrap().push(txt_line[i].clone());
1366 }
1367 } else {
1368 return Err(anyhow::anyhow!(
1369 "Lang block end is not a line: {:?}",
1370 block
1371 ));
1372 }
1373 }
1374 let ori_len = (end_index.0 - start_index.0 + 1) as isize;
1375 let new_len = nodes.len() as isize;
1376 for _ in start_index.0..=end_index.0 {
1377 output.remove(start_index.0);
1378 }
1379 let mut start_index = start_index.0;
1380 for node in nodes {
1381 output.insert(start_index, node);
1382 start_index += 1;
1383 }
1384 current_line = (current_line as isize + new_len - ori_len) as usize;
1385 } else {
1386 for node in nodes {
1388 output.insert(current_line, node);
1389 current_line += 1;
1390 }
1391 }
1392 lang_block_index = None;
1393 lang_end_block_index = None;
1394 mess = mes.next();
1395 last_tag_block_loc = None;
1396 } else if node.is_tag("selectbtn_init") {
1397 is_selectblk = true;
1398 } else if node.is_tag("selectbtn") {
1399 let lan = match lang.as_ref() {
1400 Some(l) => l.as_str(),
1401 None => {
1402 for key in node.tag_attr_keys() {
1403 if key == "label" || key == "call" {
1404 continue;
1405 }
1406 lang = Some(key.to_string());
1407 break;
1408 }
1409 match lang.as_ref() {
1410 Some(l) => l.as_str(),
1411 None => {
1412 return Err(anyhow::anyhow!(
1413 "No language found in selectbtn tag"
1414 ));
1415 }
1416 }
1417 }
1418 };
1419 let m = match mess {
1420 Some(m) => m,
1421 None => {
1422 return Err(anyhow::anyhow!("Not enough messages."));
1423 }
1424 };
1425 let mut message = m.message.clone();
1426 if let Some(repl) = replacement {
1427 for (k, v) in &repl.map {
1428 message = message.replace(k, v);
1429 }
1430 }
1431 let mut node = node.clone();
1432 node.tag_set_attr(lan, Some(message));
1433 let block = &mut output[current_line];
1434 if let ParsedLine::Line(txt_line) = block {
1435 txt_line[i] = node;
1436 } else {
1437 return Err(anyhow::anyhow!("selectbtn tag not in line: {:?}", block));
1438 }
1439 mess = mes.next();
1440 } else if node.is_tag("/selectbtn") {
1441 is_selectblk = false;
1442 } else if in_lang_block {
1443 } else if droped_lang_block {
1445 } else if is_selectblk {
1447 } else if let TxtLineNode::Tag(tag) = node {
1449 if !tag.is_blocked_name(&self.blacklist_names) {
1450 last_tag_block_loc = Some((current_line, i));
1451 }
1452 }
1453 }
1454 }
1455 current_line += 1;
1456 }
1457 let s = output.serialize();
1458 let encoded = encode_string(encoding, &s, false)?;
1459 file.write_all(&encoded)?;
1460 file.flush()?;
1461 Ok(())
1462 }
1463}
1464
1465pub fn read_tags_from_ini<P: AsRef<std::path::Path>>(path: P) -> Result<HashSet<String>> {
1467 let conf = ini::Ini::load_from_file(path)?;
1468 let set = HashSet::from_iter(conf.sections().flat_map(|s| s.map(|s| s.to_string())));
1469 eprintln!(
1470 "Read tags from ini: {}",
1471 set.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(",")
1472 );
1473 Ok(set)
1474}
1475
1476#[test]
1477fn test_xml_parser() {
1478 let data = "测试文本\nok<r a=\"b\">测试<b o=\"文本\n换行\">";
1479 let data = XMLTextParser::new(data, "en", false).parse().unwrap();
1480 assert_eq!(
1481 data,
1482 vec![
1483 ParsedLine::Line(TxtLine(vec![
1484 TxtLineNode::Tag(TagNode {
1485 name: "lang".to_string(),
1486 attributes: vec![("en".to_string(), None)],
1487 }),
1488 TxtLineNode::Text(TextNode("测试文本".to_string())),
1489 TxtLineNode::Tag(TagNode {
1490 name: "rt2".to_string(),
1491 attributes: vec![],
1492 }),
1493 ])),
1494 ParsedLine::Line(TxtLine(vec![
1495 TxtLineNode::Text(TextNode("ok".to_string())),
1496 TxtLineNode::Tag(TagNode {
1497 name: "r".to_string(),
1498 attributes: vec![("a".to_string(), Some("b".to_string()))],
1499 }),
1500 TxtLineNode::Text(TextNode("测试".to_string())),
1501 TxtLineNode::Tag(TagNode {
1502 name: "b".to_string(),
1503 attributes: vec![("o".to_string(), Some("文本\n换行".to_string()))],
1504 }),
1505 TxtLineNode::Tag(TagNode {
1506 name: "/lang".to_string(),
1507 attributes: vec![],
1508 }),
1509 ])),
1510 ],
1511 );
1512}